#include <vector>
#include <string>
#include <iostream>

#include "tinyxml2.hpp"
#include "ISerializable.hpp"
#include "StringConverters.hpp"

using namespace std;
using namespace tinyxml2;

Serializable::Serializable() 
{
    
}
Serializable::~Serializable() 
{
    
}


XMLElement* Serializable::Serialize(XMLDocument* doc, string name, XMLElement* parent)
{
    XMLElement* root = doc->NewElement(name.c_str());
	
    for(int i=0; i<_variables.size(); i++)
    {
        XMLElement* element = doc->NewElement(_variables[i].name.c_str());
        
        XMLText* valueText;
        string valueStr = "";
        switch(_variables[i].type)
        {
            case BOOL:
                element->SetAttribute("type", "BOOL");
                valueStr = bool_to_string(*(static_cast<bool*>(_variables[i].value)));
                break;
            case INT:
                element->SetAttribute("type", "INT");
                valueStr = int_to_string(*(static_cast<int*>(_variables[i].value)));
                break;
            case FLOAT:
                element->SetAttribute("type", "FLOAT");
                valueStr = float_to_string(*(static_cast<float*>(_variables[i].value)));
                break;
            case STRING:
                element->SetAttribute("type", "STRING");
                valueStr = *(static_cast<string*>(_variables[i].value));
                break;
            case BOOL_ARR:
            {
                element->SetAttribute("type", "BOOL_ARR");
                bool* boolarr = static_cast<bool*>(_variables[i].value);
                for(int j=0; j<_variables[i].valCount; j++)
                {
                    string text = bool_to_string(boolarr[j]);
                    XMLElement* temp = doc->NewElement(int_to_string(j).c_str());
                    XMLText* valTex = doc->NewText(text.c_str());
                    temp->LinkEndChild(valTex);
                    element->LinkEndChild(temp);
                }
                break;
            }
            case INT_ARR:
            {
                element->SetAttribute("type", "INT_ARR");
                int* intarr = static_cast<int*>(_variables[i].value);
                for(int j=0; j<_variables[i].valCount; j++)
                {
                    string text = int_to_string(intarr[j]);
                    XMLElement* temp = doc->NewElement(int_to_string(j).c_str());
                    XMLText* valTex = doc->NewText(text.c_str());
                    temp->LinkEndChild(valTex);
                    element->LinkEndChild(temp);
                }
                break;
            }
            case FLOAT_ARR:
            {
                element->SetAttribute("type", "FLOAT_ARR");
                float* floatarr = static_cast<float*>(_variables[i].value);
                for(int j=0; j<_variables[i].valCount; j++)
                {
                    string text = float_to_string(floatarr[j]);
                    XMLElement* temp = doc->NewElement(int_to_string(j).c_str());
                    XMLText* valTex = doc->NewText(text.c_str());
                    temp->LinkEndChild(valTex);
                    element->LinkEndChild(temp);
                }
                break;
            }
            case STRING_ARR:
            {
                element->SetAttribute("type", "STRING_ARR");
                std::string* stringarr = static_cast<std::string*>(_variables[i].value);
                for(int j=0; j<_variables[i].valCount; j++)
                {
                    std::string text = stringarr[j];
                    XMLElement* temp = doc->NewElement(int_to_string(j).c_str());
                    XMLText* valTex = doc->NewText(text.c_str());
                    temp->LinkEndChild(valTex);
                    element->LinkEndChild(temp);
                }
                break;
            }
        }
        if(valueStr.compare("")!=0)
        {
            valueText = doc->NewText(valueStr.c_str());
            element->LinkEndChild(valueText);
        }
        root->LinkEndChild(element);
    }
    
    if(parent==NULL)
        doc->LinkEndChild(root);
    else
        parent->LinkEndChild(root);
    
    return root;
}


void Serializable::Deserialize(XMLElement* root)
{    
    for(int i=0; i<_variables.size(); i++)
    {
        XMLElement* child = root->FirstChildElement(_variables[i].name.c_str());
        if(child!=NULL)
            switch(_variables[i].type)
            {
                case BOOL:
                    if(child->Attribute("type", "BOOL"))
                        *(static_cast<bool*>((_variables[i].value))) = string_to_bool(child->GetText());
                    break;
                case INT:
                    if(child->Attribute("type", "INT"))
                        *(static_cast<int*>((_variables[i].value))) = atoi(child->GetText());
                    break;
                case FLOAT:
                    if(child->Attribute("type", "FLOAT"))
                        *(static_cast<float*>((_variables[i].value))) = atof(child->GetText());
                    break;
                case STRING:
                    if(child->Attribute("type", "STRING"))
                        *(static_cast<string*>((_variables[i].value))) = child->GetText();
                    break;
                case BOOL_ARR:
                    if(child->Attribute("type", "BOOL_ARR"))
                    {
                        bool* boolarr = static_cast<bool*>(_variables[i].value);
                        for(int j=0; j<_variables[i].valCount; j++)
                        {
                            string name = int_to_string(j);
                            boolarr[j] = string_to_bool(child->FirstChildElement(name.c_str())->GetText());
                        }
                    }
                    break;
                case INT_ARR:
                    if(child->Attribute("type", "INT_ARR"))
                    {
                        int* intarr = static_cast<int*>(_variables[i].value);
                        for(int j=0; j<_variables[i].valCount; j++)
                        {
                            string name = int_to_string(j);
                            intarr[j] = atoi(child->FirstChildElement(name.c_str())->GetText());
                        }
                    }
                    break;
                case FLOAT_ARR:
                    if(child->Attribute("type", "FLOAT_ARR"))
                    {
                        float* floatarr = static_cast<float*>(_variables[i].value);
                        for(int j=0; j<_variables[i].valCount; j++)
                        {
                            string name = int_to_string(j);
                            floatarr[j] = atof(child->FirstChildElement(name.c_str())->GetText());
                        }
                    }
                    break;
                case STRING_ARR:
                    if(child->Attribute("type", "STRING_ARR"))
                    {
                        string* stringarr = static_cast<string*>(_variables[i].value);
                        for(int j=0; j<_variables[i].valCount; j++)
                        {
                            string name = int_to_string(j);
                            stringarr[j] = child->FirstChildElement(name.c_str())->GetText();
                        }
                    }
                    break;
            }
    }
}
void Serializable::Deserialize(XMLDocument* doc)
{
    XMLElement* root = doc->RootElement();
    Deserialize(root);
}


void Serializable::MarkSerializable(std::string name, bool* value)
{
    void* ptr = value;
    SerializedVar temp(name, ptr, BOOL);
    _variables.push_back(temp);
}
void Serializable::MarkSerializable(std::string name, int* value)
{
    void* ptr = value;
    SerializedVar temp(name, ptr, INT);
    _variables.push_back(temp);
}
void Serializable::MarkSerializable(std::string name, float* value)
{
    void* ptr = value;
    SerializedVar temp(name, ptr, FLOAT);
    _variables.push_back(temp);
}
void Serializable::MarkSerializable(std::string name, string* value)
{
    void* ptr = value;
    SerializedVar temp(name, ptr, STRING);
    _variables.push_back(temp);
}
void Serializable::MarkSerializable(std::string name, int valCount, bool* array)
{
    void* ptr = array;
    SerializedVar temp(name, ptr, BOOL_ARR, valCount);
    _variables.push_back(temp);
}
void Serializable::MarkSerializable(std::string name, int valCount, int* array)
{
    void* ptr = array;
    SerializedVar temp(name, ptr, INT_ARR, valCount);
    _variables.push_back(temp);
}
void Serializable::MarkSerializable(std::string name, int valCount, float* array)
{
    void* ptr = array;
    SerializedVar temp(name, ptr, FLOAT_ARR, valCount);
    _variables.push_back(temp);
}
void Serializable::MarkSerializable(std::string name, int valCount, std::string* array)
{
    void* ptr = array;
    SerializedVar temp(name, ptr, STRING_ARR, valCount);
    _variables.push_back(temp);
}
